0%

signal — Set handlers for asynchronous events

原文:signal — Set handlers for asynchronous events

Python的 signal 模块为使用信号处理器提供了一些途径。关于信号和处理器的工作过程有一些规则需要注意:

  • 除非明确地重置处理器,否则一旦为一个特定的信号设置了处理器,该处理器就将一直有效(Python模仿了BSD的风格接口而不管底层是如何实现的)。唯一例外的是SIGCHLD信号处理器,其由底层实现决定。

  • 无法从critical sections临时阻塞信号(因为在类UNIX系统中都不支持)。

  • 尽管Python信号处理器能在接收到相应信号时立即被异步调用,但实际上是在Python解释器的原子指令之间被调用(注: 意思就是Python信号处理器的调用必须在一条“原子指令”执行完之后才能被调用)。这就意味着若信号到达时Python解释器正在执行一个长时间的纯C计算(比如正则表达式处理一大段文本),那么信号处理器的调用将会被推迟不确定时间。

  • 当一个信号在I/O操作期间到来,信号处理器调用完成后可能导致I/O操作抛出异常。这个依赖于顶层Unix系统的系统调用中断语义。

  • 因为 C 信号处理器总是会返回,所以就没有处理类似 SIGFPE 和 SIGSEGV 导致的同步错误(注: 在POSIX兼容的平台上,SIGFPE(floating-point exception,浮点异常)是当一个进程执行了一个错误的算术操作时发送给它的信号,SIGSEGV(egmentation violation, 段违例)是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。)。

  • Python默认设置了一些信号处理器:SIGPIPE信号会被忽略(所以在管道或者套接字上 “写” 错误时将抛出一个普通的Python异常);SIGINT信号会被包装成一个 KeyboardInterrupt 异常。这些信号处理器都可以被overriden。

  • 多线程信号编程时需要注意一些问题。在同时使用多线程和信号时需要记住的基本规则:signal() 只能在主线程中执行;任何线程都可以执行 alarm(), getsignal(), pause(), setitimer()或者 getitimer();只有主线程可以设置新的信号处理器,也只有主线程可以接受到信号(尽管底层线程实现支持将信号发送给任何一个独立线程,但是Python的信号模块强制实现只能主线程能接受信号)。这就意味着在Python中信号不能用于线程间通信,使用锁来代替。

变量

signal.SIG_DFL

  • 这是两个标准信号处理选项之一,该处理选项将简单地执行默认的信号处理函数。例如,在大多数系统中收到 SIGQUIT 信号后转存文件(dump core )并退出,而忽略掉 SIGCHLD 信号。

signal.SIG_IGN

  • 这是另一个默认信号处理器,该处理选项将会忽略指定的信号。例如,signal(SIGCHLD, SIG_IGN)

SIG

  • All the signal numbers are defined symbolically. For example, the hangup signal is defined as signal.SIGHUP; the variable names are identical to the names used in C programs, as found in . The Unix man page for ‘signal()‘ lists the existing signals (on some systems this is signal(2), on others the list is in signal(7)). Note that not all systems define the same set of signal names; only those names defined by the system are defined by this module. (注:每一个信号名称是有一个代表正整数的宏来表示,但是不应该试图去推测宏代表的具体数字,而是直接使用信号名称。这是因为这个数字会随着系统的不同或同一系统的不同版本而不同,但是名称还算是标准化和统一的) signal.CTRL_C_EVENT

  • Windows 上与 CTRL+C 相对应的控制事件。只能与 os.kill() 配合使用。
  • Availability: Windows.
  • New in version 2.7.

signal.CTRL_BREAK_EVENT

  • Windows 上与 CTRL+BREAK 相对应的控制事件。只能与 os.kill() 配合使用。
  • Availability: Windows.
  • New in version 2.7.

signal.NSIG

  • 比系统中定义的信号数大1的整数(注: 系统中定义的信号数量,由于信号是从0开始连续分配的,所以NSIG比系统所定义的最大信号数值大1)。

signal.ITIMER_REAL

  • 计时器的值实时递减(以系统实时时间来计算 ),超时发送 SIGALRM 信号。

signal.ITIMER_VIRTUAL

  • 进程执行时递减计时器的值(只计算(用户态)进程的执行时间),超时发送 SIGVTALRM 信号。

signal.ITIMER_PROF

  • 进程和系统执行时都递减计时器的值。结合 ITIMER_VIRTUAL, 常常被用于分析程序在用户态和内核态花费的时间。超时发送 SIGPROF 信号。

异常

exception signal.ItimerError

  • setitimer() 和 getitimer() 底层实现抛出该异常。当调用 setitimer() 传递一个不合法的间隔时间或者负数时间时会抛出该异常。该异常继承自 IOError。

函数

signal.alarm(time)

  • 如果 time 非0,函数将在 time 秒后向进程发送 SIGALRM 信号。任何时候只能有1个 alarm ,所以一旦调用该函数,之前设置的 alarm 都会被取消。函数返回前一次设置 alarm 到现在的时间。如果 time 为0,所有的 alarm 都会被取消。当之前没有设置过 alarm 时,函数返回0.(See the Unix man page alarm(2).)
  • Availability: Unix.

signal.getsignal(signalnum)

  • 调用该函数将返回指定信号的处理器,处理器可能为一个可调用的 Python 对象、signal.SIG_IGN、signal.SIG_DFL或者None。signal.SIG_IGN表示之前会忽略所有信号,ignal.SIG_DFL使用默认信号处理器处理信号,None表示之前没有为该信号设置处理器。

signal.pause()

  • 挂起进程以等待信号,信号到来时相应的处理器将被调用。无返回值,Windows 平台不可用 (See the Unix man page signal(2)) 。

signal.setitimer(which, seconds[, interval])

  • 设置指定 seconds (与 alarm() 函数不同, seconds 可以为浮点数)后开始触发的间隔定时器,间隔定时器的类型由 which 参数指定,可以为 signal.ITIMER_REAL, signal.ITIMER_VIRTUAL 或者 signal.ITIMER_PROF。seconds 设置为0可以清除指定类型的间隔定时器。当一个间隔定时器触发时,将会向进程发送信号,具体的信号由间隔定时器指定的类型决定。
    The old values are returned as a tuple: (delay, interval).
    Attempting to pass an invalid interval timer will cause an ItimerError.
  • Availability: Unix.
  • New in version 2.6.

signal.getitimer(which)

  • 返回指定类型间隔定时器设置的间隔时间。
  • Availability: Unix.
  • New in version 2.6.

signal.set_wakeup_fd(fd)

  • 设置唤醒描述符为给定的描述符 fd。当收到信号后,一个 null (‘\0’)字节将被写入该描述符。函数库通过使用该函数来唤醒 poll 或者 select 调用以处理信号。该函数调用返回之前设置的唤醒描述符。唤醒描述符 fd 必须是非阻塞的,在再次调用 poll 或者 select 之前由函数库负责删除其中的任何字节。在多线程中,该函数仅允许在主线程中调用,尝试在非主线程嗲用时将引发 ValueError 异常。
  • New in version 2.6.

signal.siginterrupt(signalnum, flag)

  • 改变系统调用的重试的行为:如果 flag 为 False,当指定信号中断系统调用后会自动重试,否则将会直接中断。函数无返回值。注意:使用 signal() 函数注册一个信号处理器会隐式使用 true 为指定信号调用 siginterrupt() 函数。
  • New in version 2.6.

signal.signal(signalnum, handler)

  • 调用该函数为指定的信号设置信号处理器,信号处理器可以为一个具有2个参数的可调用对象、signal.SIG_IGN 或者 signal.SIG_DFL。函数返回之前设置的信号处理器。
  • 在多线程中,该函数仅允许在主线程中调用,尝试在非主线程嗲用时将引发 ValueError 异常。
  • 信号处理器具有2个参数:信号number、当前栈帧(None或者 frame 对象)
  • 在Windows平台上仅 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM 可用,尝试使用其他信号将会引发 ValueError 异常。

示例

Here is a minimal example program. It uses the alarm() function to limit the time spent waiting to open a file; this is useful if the file is for a serial device that may not be turned on, which would normally cause the os.open() to hang indefinitely. The solution is to set a 5-second alarm before opening the file; if the operation takes too long, the alarm signal will be sent, and the handler raises an exception.

1
2
3
4
5
6
7
8
9
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise IOError("Couldn't open device!")

# Set the signal handler and a 5-second alarmsignal.signal(signal.SIGALRM, handler)signal.alarm(5)

# This open() may hang indefinitelyfd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0) # Disable the alarm